package com.example.protocol;

import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class GyroscopeController {
    private final IObserver observer;

    private SerialNative serialNative;
    private final OutputStream outputStream;
    private final InputStream inputStream;
    private final Executor userThread = Executors.newCachedThreadPool();
    private Thread fixThread;

    private static final int CAP = 1024;     // 初始区大小
    private static final int LIMIT = 896;   // 溢出告警线
    private static final int RW_LEN = 128;  // 读 &写缓存大小

    private final byte[] reader = new byte[CAP];  // 数据读取缓存区域
    private int off = 0; // 有效数据开头(包含)
    private int end = 0; // 有效数据结尾(排除)

    private byte state = IDLE;
    private static final byte IDLE = 0;
    private static final byte DIS = 1;
    private static final byte CON = 2;


    private static final byte HEAD = 0x55;


    private GyroscopeController(Builder builder) throws IOException {
        this.observer = builder.observer;
        serialNative = new SerialNative(new File(builder.portName), builder.baud, 0);
        outputStream = serialNative.getOutputStream();
        inputStream = serialNative.getInputStream();

        // 死循环
        fixThread = new Thread(() -> {
            while (!Thread.interrupted()) {
                try {
                    cacheMonitor();
                    if (inputStream != null) end += inputStream.read(reader, end, RW_LEN);
                    this.dealNewData(); // 处理读取到的数据
                    if (state == DIS) {
                        if (observer != null) {
                            byte[] status = {state};
                            userThread.execute(() -> observer.dataNotify(ActCode.ACT_CON_STATUS, status));
                        }
                    }
                    state = CON;
                } catch (IOException ignored) {
                    Log.e("TAG", "serial error");
                    off = end = 0;
                }
            }
        });

        fixThread.start();

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if (state == IDLE) {
                    if (observer != null) {
                        state = DIS;
                        byte[] status = {state};
                        userThread.execute(() -> observer.dataNotify(ActCode.ACT_CON_STATUS, status));
                    }
                }
                if (state == CON) state = IDLE;
            }
        }, 20_000L, 15_000L);
    }

    private void dealNewData() {
        while (off < end) {
            if (reader[off] == HEAD && reader[off + 1] == HEAD) {
                int ID = reader[off + 2];
                int len = (reader[off + 3]);
                byte[] data = new byte[len + 5];
                System.arraycopy(reader, off, data, 0, data.length);
                off += len + 5;
                userThread.execute(() -> observer.dataNotify(ID, data));
            } else {
                off++;
            }
        }
    }


    /*缓存监控*/
    private void cacheMonitor() {
        if (end > LIMIT) { // 缓存区域溢出告警
            if (end - off > 0) {
                System.arraycopy(reader, off, reader, 0, end - off);
            }
            end = end - off;
            off = 0; // 回归
        }
    }


    public static class Builder {
        private IObserver observer;
        private String portName = "/dev/ttyO4";
        private int baud = 115200;


        public Builder configPortName(String portName) {
            this.portName = portName;
            return this;
        }

        public Builder configBaud(int baud) {
            this.baud = baud;
            return this;
        }

        public Builder configObserver(IObserver observer) {
            this.observer = observer;
            return this;
        }

        public GyroscopeController build() throws IOException {
            return new GyroscopeController(this);
        }
    }
}
